// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.

namespace WixTest.Utilities
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Management;

    public static class FileUtilities
    {
        /// <summary>
        /// Recursivelly copy contents of a directory to another directory.
        /// </summary>
        /// <param name="sourceDirectory">Source Directory</param>
        /// <param name="destinationDirectory">Destination Directory</param>
        public static void CopyDirectory(string sourceDirectory, string destinationDirectory)
        {
            string[] files = Directory.GetFiles(sourceDirectory);
            foreach (string file in files)
            {
                File.Copy(file, Path.Combine(destinationDirectory, Path.GetFileName(file)), true);
            }

            string[] subDirectories = Directory.GetDirectories(sourceDirectory);

            foreach (string subDirectory in subDirectories)
            {
                DirectoryInfo subDirectoryInfo = new DirectoryInfo(subDirectory);
                string destinationSubDirectory = Path.Combine(destinationDirectory, subDirectoryInfo.Name);
                if (!Directory.Exists(destinationSubDirectory))
                {
                    Directory.CreateDirectory(destinationSubDirectory);
                }

                FileUtilities.CopyDirectory(subDirectory, destinationSubDirectory);
            }
        }

        /// <summary>
        /// Creates a new folder if one does not already exist
        /// </summary>
        /// <param name="outputDirectory">The directory to create</param>
        /// <remarks>
        /// These directories are often used for intermediate output generated by Wix.
        /// Eg. During a compile & link, .wixobjs are created and stored in a subfolder
        /// </remarks>
        public static void CreateOutputDirectory(string outputDirectory)
        {
            if (!Directory.Exists(outputDirectory))
            {
                Directory.CreateDirectory(outputDirectory);
            }
        }

        /// <summary>
        /// Deletes all of the files in outputDirectory
        /// </summary>
        /// <param name="outputDirectory">The directory to clean</param>
        public static void CleanOutputDirectory(string outputDirectory)
        {
            if (Directory.Exists(outputDirectory))
            {
                Directory.Delete(outputDirectory, true);
            }
        }


        /// <summary>
        /// Returns a unique file or folder name
        /// </summary>
        /// <param name="root">The root folder to get a unique file name under</param>
        /// <returns>A unique file or folder name</returns>
        public static string GetUniqueFileName(string root)
        {
            string uniqueFileName;

            do
            {
                uniqueFileName = Path.Combine(root, Path.GetRandomFileName());
            } while (Directory.Exists(uniqueFileName) || File.Exists(uniqueFileName));

            return uniqueFileName;
        }

        /// <summary>
        /// Returns a unique file or folder name rooted under the TEMP directory
        /// </summary>
        /// <returns>A unique file or folder name</returns>
        public static string GetUniqueFileName()
        {
            string root = Path.GetTempPath();

            return FileUtilities.GetUniqueFileName(root);
        }

        /// <summary>
        ///  Set the current directory to a diffrent directory
        /// </summary>
        /// <param name="directoryName">the directory name to set to</param>
        /// <returns>The original value of the current directory</returns>
        public static string SetWorkingDirectory(string directoryName)
        {
            string expandedDirectoryName = Environment.ExpandEnvironmentVariables(directoryName);

            if (Environment.CurrentDirectory != expandedDirectoryName)
            {
                string originalWorkingdirectory = Environment.CurrentDirectory;

                Directory.CreateDirectory(expandedDirectoryName);
                Environment.CurrentDirectory = expandedDirectoryName;

                Console.WriteLine("The Current Directory is {0}", expandedDirectoryName);
                return originalWorkingdirectory;
            }
            else
            {
                return expandedDirectoryName;
            }
        }

        /// <summary>
        /// Create a share for a given local path, and sets read/write access rights to everybody
        /// </summary>
        /// <param name="sharePath">local path of directory to be shared</param>
        /// <param name="shareName">Share Name</param>
        public static bool CreateShare(string sharePath, string shareName)
        {
            ManagementObject trustee = new ManagementClass("Win32_Trustee").CreateInstance();
            trustee["Domain"] = null;
            trustee["Name"] = "Everyone";
            trustee["SID"] = new Object[] { 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; // everybody

            ManagementObject ace = new ManagementClass("Win32_Ace").CreateInstance();
            ace["AccessMask"] = 2032127; // full access
            ace["AceFlags"] = 0x1 | 0x2; //OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
            ace["AceType"] = 0; //Access Allowed
            ace["Trustee"] = trustee;

            ManagementObject dacl = new ManagementClass("Win32_SecurityDescriptor").CreateInstance();
            dacl["DACL"] = new Object[] { ace };

            ManagementClass share = new ManagementClass("Win32_Share");
            ManagementBaseObject inParams = share.GetMethodParameters("Create");
            ManagementBaseObject outParams;
            inParams["Description"] = "Create Share Folder";
            inParams["Name"] = shareName;
            inParams["Path"] = sharePath;
            inParams["Type"] = 0x0; // Disk Drive
            inParams["Access"] = dacl;
            outParams = share.InvokeMethod("Create", inParams, null);

            // Check to see if the method invocation was successful
            if ((uint)(outParams.Properties["ReturnValue"].Value) != 0)
            {
                //throw new Exception("Unable to share directory.");
                return false;
            }
            return true;
        }

        /// <summary>
        /// Create a random file with a given size in temp directory
        /// </summary>
        /// <param name="size">required size in bytes</param>
        /// <returns>File path to the new file</returns>
        public static string CreateSizedFile(int size)
        {
            string fullfilename = FileUtilities.GetUniqueFileName();

            File.WriteAllBytes(fullfilename, new byte[size]);

            return fullfilename;
        }

        /// <summary>
        /// Gets the directory containing the given <paramref name="filename"/>.
        /// </summary>
        /// <param name="filename">The filename to find in the ancestor directories.</param>
        /// <param name="parentDirectory">The parent directory from which to start the search. The default is the current working directory.</param>
        /// <returns>The directory containing the given <paramref name="filename"/>, or null if the file was not found in any ancestors.</returns>
        public static string GetDirectoryNameOfFileAbove(string filename, string parentDirectory = null)
        {
            if (String.IsNullOrEmpty(parentDirectory))
            {
                parentDirectory = Environment.CurrentDirectory;
            }

            if (!Directory.Exists(parentDirectory))
            {
                throw new DirectoryNotFoundException("The parent directory was not found or is not a directory.");
            }

            DirectoryInfo dir = new DirectoryInfo(parentDirectory);
            do
            {
                FileInfo[] files = dir.GetFiles(filename, SearchOption.TopDirectoryOnly);
                if (null != files && 0 < files.Length)
                {
                    return dir.FullName;
                }
            }
            while (null != (dir = dir.Parent));

            return null;
        }
    }
}
